#ifndef __Vector2_hpp__
#define __Vector2_hpp__

#include "Math.hpp"

namespace ge {

//----------------------------------------------------------------------------//
// Vector2
//----------------------------------------------------------------------------//

class Vector2
{
 public:

     Vector2( void );

     Vector2( const Vector2& _v );

     Vector2( float _s );

     Vector2( float _x, float _y );

     Vector2( const float _v[2] );

     Vector2& operator = ( const Vector2& _v );

     Vector2& operator = ( float _s );

     float& operator [] ( uint _index );

     const float& operator [] ( uint _index ) const;

     float* operator * ( void );

     const float* operator * ( void ) const;

     bool operator == ( const Vector2& _v ) const;

     bool operator < ( const Vector2& _v ) const;

     bool operator <= ( const Vector2& _v ) const;

     bool operator != ( const Vector2& _v ) const;

     bool operator > ( const Vector2& _v ) const;

     bool operator >= ( const Vector2& _v ) const;

     Vector2 operator - ( void ) const;

     const Vector2& operator + ( void ) const;

     Vector2 operator + ( const Vector2& _v ) const;

     Vector2 operator - ( const Vector2& _v ) const;

     Vector2 operator * ( const Vector2& _v ) const;

     Vector2 operator / ( const Vector2& _v ) const;

     Vector2 operator + ( float _s ) const;

     Vector2 operator - ( float _s ) const;

     Vector2 operator * ( float _s ) const;

     Vector2 operator / ( float _s ) const;

     friend Vector2 operator + ( float _s, const Vector2& _v );

     friend Vector2 operator - ( float _s, const Vector2& _v );

     friend Vector2 operator * ( float _s, const Vector2& _v );

     friend Vector2 operator / ( float _s, const Vector2& _v );

     Vector2& operator += ( const Vector2& _v );

     Vector2& operator -= ( const Vector2& _v );

     Vector2& operator *= ( const Vector2& _v );

     Vector2& operator /= ( const Vector2& _v );

     Vector2& operator += ( float _s );

     Vector2& operator -= ( float _s );

     Vector2& operator *= ( float _s );

     Vector2& operator /= ( float _s );

     Vector2& Set( float _s );

     Vector2& Set( float _x, float _y );

     Vector2& FromPtr( const float _v[2] );

     Vector2& Set( const Vector2& _v );

     Vector2& SetZero( void );

     Vector2& SetIdentity( void );

     Vector2& SetAxis( uint _axis, float _value );

     float& Axis( uint _axis ); 

     const float& Axis( uint _axis ) const; 

     float LengthSq( void ) const;

     float Length( void ) const;

     float LengthInv( void ) const;

     float Distance( const Vector2& _v ) const;

     float DistanceSq( const Vector2& _v ) const;

     float Dot( const Vector2& _v ) const;

     float Cross( const Vector2& _v ) const;

     Vector2& Normalize( void );

     Vector2& SafeNormalize( void );

     Vector2& SafeNormalize( float& _length );

     Vector2 NormalizedCopy( void ) const;

     Vector2 MidPoint( const Vector2& _v ) const;

     Vector2& SetMin( const Vector2& _v );

     Vector2& SetMin( const Vector2& _v1, const Vector2& _v2 );

     Vector2& SetMax( const Vector2& _v );

     Vector2& SetMax( const Vector2& _v1, const Vector2& _v2 );

     Vector2 Perpendicular( void ) const;
       
     Vector2 RandomDeviant( float _a ) const;

     Vector2 Reflect( const Vector2& _n ) const;

     Vector2 NormalizedReflect( const Vector2& _n ) const;

 public:

     static const Vector2 Zero;
     static const Vector2 UnitX;
     static const Vector2 UnitY;
     static const Vector2 NegativeUnitX;
     static const Vector2 NegativeUnitY;
     static const Vector2 UnitScale;
     static const Vector2 Identity;

 public:

     union
     {
          struct { float x, y; };
          float v[2];
     };
};

//----------------------------------------------------------------------------//
//
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
inline Vector2::Vector2( void ) 
{
}
//----------------------------------------------------------------------------//
inline Vector2::Vector2( const Vector2& _v ) : x( _v.x ), y( _v.y )
{
}
//----------------------------------------------------------------------------//
inline Vector2::Vector2( float _s ) : x( _s ), y( _s )
{
}
//----------------------------------------------------------------------------//
inline Vector2::Vector2( float _x, float _y ) : x( _x ), y( _y )
{
}
//----------------------------------------------------------------------------//
inline Vector2::Vector2( const float _v[2] ) : x( _v[0] ), y( _v[1] )
{
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator = ( const Vector2& _v )
{
     x = _v.x;
     y = _v.y;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator = ( float _s )
{
     x = _s;
     y = _s;
     return *this;
}
//----------------------------------------------------------------------------//
inline float& Vector2::operator [] ( uint _index )
{
     return v[_index];
}
//----------------------------------------------------------------------------//
inline const float& Vector2::operator [] ( uint _index ) const
{
     return v[_index];
}
//----------------------------------------------------------------------------//
inline float* Vector2::operator * ( void )
{
     return v;
}
//----------------------------------------------------------------------------//
inline const float* Vector2::operator * ( void ) const
{
     return v;
}
//----------------------------------------------------------------------------//
inline bool Vector2::operator == ( const Vector2& _v ) const
{
     return x == _v.x && y == _v.y;
}
//----------------------------------------------------------------------------//
inline bool Vector2::operator < ( const Vector2& _v ) const
{
     return x < _v.x && y < _v.y;
}
//----------------------------------------------------------------------------//
inline bool Vector2::operator <= ( const Vector2& _v ) const
{
     return x <= _v.x && y <= _v.y;
}
//----------------------------------------------------------------------------//
inline bool Vector2::operator != ( const Vector2& _v ) const
{
     return !(*this == _v);
}
//----------------------------------------------------------------------------//
inline bool Vector2::operator > ( const Vector2& _v ) const
{
     return !(*this <= _v);
}
//----------------------------------------------------------------------------//
inline bool Vector2::operator >= ( const Vector2& _v ) const
{
     return !(*this < _v);
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator - ( void ) const
{
     return Vector2( -x, -y );
}
//----------------------------------------------------------------------------//
inline const Vector2& Vector2::operator + ( void ) const
{
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator + ( const Vector2& _v ) const
{
     return Vector2( x + _v.x, y + _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator - ( const Vector2& _v ) const
{
     return Vector2( x - _v.x, y - _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator * ( const Vector2& _v ) const
{
     return Vector2( x * _v.x, y * _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator / ( const Vector2& _v ) const
{
     return Vector2( x / _v.x, y / _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator + ( float _s ) const
{
     return Vector2( x + _s, y + _s );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator - ( float _s ) const
{
     return Vector2( x - _s, y - _s );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator * ( float _s ) const
{
     return Vector2( x * _s, y * _s );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::operator / ( float _s ) const
{
     _s = 1.0f / _s;
     return Vector2( x * _s, y * _s );
}
//----------------------------------------------------------------------------//
inline Vector2 operator + ( float _s, const Vector2& _v )
{
     return Vector2( _s + _v.x, _s + _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2 operator - ( float _s, const Vector2& _v )
{
     return Vector2( _s - _v.x, _s - _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2 operator * ( float _s, const Vector2& _v )
{
     return Vector2( _s * _v.x, _s * _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2 operator / ( float _s, const Vector2& _v )
{
     return Vector2( _s / _v.x, _s / _v.y );
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator += ( const Vector2& _v )
{
     x += _v.x;
     y += _v.y;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator -= ( const Vector2& _v )
{
     x -= _v.x;
     y -= _v.y;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator *= ( const Vector2& _v )
{
     x *= _v.x;
     y *= _v.y;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator /= ( const Vector2& _v )
{
     x /= _v.x;
     y /= _v.y;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator += ( float _s )
{
     x += _s;
     y += _s;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator -= ( float _s )
{
     x -= _s;
     y -= _s;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator *= ( float _s )
{
     x *= _s;
     y *= _s;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::operator /= ( float _s )
{
     _s = 1.0f / _s;
     x *= _s;
     y *= _s;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::Set( float _s )
{
     x = _s;
     y = _s;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::Set( float _x, float _y )
{
     x = _x;
     y = _y;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::FromPtr( const float _v[2] )
{
     x = _v[0];
     y = _v[1];
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::Set( const Vector2& _v )
{
     x = _v.x;
     y = _v.y;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SetZero( void )
{
     x = 0.0f;
     x = 0.0f;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SetIdentity( void )
{
     x = 1.0f;
     x = 1.0f;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SetAxis( uint _axis, float _value )
{
     v[_axis] = _value;
     return *this;
}
//----------------------------------------------------------------------------//
inline float& Vector2::Axis( uint _axis ) 
{
     return v[_axis];
}
//----------------------------------------------------------------------------//
inline const float& Vector2::Axis( uint _axis ) const
{
     return v[_axis];
}
//----------------------------------------------------------------------------//
inline float Vector2::LengthSq( void ) const
{
     return x * x + y * y;
}
//----------------------------------------------------------------------------//
inline float Vector2::Length( void ) const
{
     return Math::Sqrt( x * x + y * y );
}
//----------------------------------------------------------------------------//
inline float Vector2::LengthInv( void ) const
{
     return Math::RSqrt( x * x + y * y );
}
//----------------------------------------------------------------------------//
inline float Vector2::Distance( const Vector2& _v ) const
{
     return ( *this - _v ).Length();
}
//----------------------------------------------------------------------------//
inline float Vector2::DistanceSq( const Vector2& _v ) const
{
     return ( *this - _v ).LengthSq();
}
//----------------------------------------------------------------------------//
inline float Vector2::Dot( const Vector2& _v ) const
{
     return x * _v.x + y * _v.y;
}
//----------------------------------------------------------------------------//
inline float Vector2::Cross( const Vector2& _v ) const
{
     return x * _v.y - y * _v.x;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::Normalize( void )
{
    return (*this *= LengthInv());
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SafeNormalize( void )
{
     float _length = LengthSq();

     if( _length > Math::EpsilonSq )
     {
         _length = Math::RSqrt( _length );
         x *= _length;
         y *= _length;
     }

     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SafeNormalize( float& _length )
{
     _length = LengthSq();

     if( _length > Math::EpsilonSq )
     {
         _length = Math::RSqrt( _length );
         x *= _length;
         y *= _length;
     }

     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::NormalizedCopy( void ) const
{
     return Vector2( *this ).Normalize();
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::MidPoint( const Vector2& _v ) const
{
    return ( (*this) + _v ) * 0.5f;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SetMin( const Vector2& _v )
{
     if( _v.x < x ) x = _v.x;
     if( _v.y < y ) y = _v.y;

     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SetMin( const Vector2& _v1, const Vector2& _v2 )
{
     x = Min( _v1.x, _v2.x );
     y = Min( _v1.y, _v2.y );

     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SetMax( const Vector2& _v )
{
     if( _v.x > x ) x = _v.x;
     if( _v.y > y ) y = _v.y;

     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2& Vector2::SetMax( const Vector2& _v1, const Vector2& _v2 )
{
     x = Max( _v1.x, _v2.x );
     y = Max( _v1.y, _v2.y );

     return *this;
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::Perpendicular( void ) const
{
     return Vector2( -y, x );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::RandomDeviant( float _a ) const
{
     float _s, _c;

     Math::SinCos( _a * Math::UnitRandom() * Math::DoublePi, _s, _c );

     return Vector2( _c * x - _s * y, _s * x + _c * y );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::Reflect( const Vector2& _n ) const
{
     return Vector2( (*this) - ( 2.0f * Dot( _n ) * _n ) );
}
//----------------------------------------------------------------------------//
inline Vector2 Vector2::NormalizedReflect( const Vector2& _n ) const
{
     return Reflect( _n ).Normalize();
}
//----------------------------------------------------------------------------//

} // namespace ge

#endif//__Vector2_hpp__
